#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdarg.h>
#include <sys/mman.h>
#include <errno.h>

#include <stat_tools.h>
#include <log.h>
#include <Singleton.h>
#include <cgi_control.h>

HTTP_SVR_NS_BEGIN

#define MSG_SIZE 10240

CTxnStatTool::CTxnStatTool (void) :
_statinfo (NULL),
_cgi_control (NULL),
last_web_req_count (0),
last_web_rsp_count (0),
last_helper_req_count (0),
last_helper_rsp_count (0)
{
    memset (last_cgi_req_count, 0x00, sizeof(last_cgi_req_count));
    memset (last_cgi_rsp_count, 0x00, sizeof(last_cgi_rsp_count));
}

CTxnStatTool::~CTxnStatTool (void)
{
    TCGIControl::Destroy();
}

CTxnStatTool* CTxnStatTool::Instance (void)
{
    return CSingleton<CTxnStatTool>::Instance ();
}

void CTxnStatTool::Destroy (void)
{
    return CSingleton<CTxnStatTool>::Destroy ();
}

static void* map_file (const char* filename, int size)
{
    int fd = ::open(filename, O_RDWR|O_CREAT, 0666);
    void *map = NULL;

    if(fd >= 0)
    {
        if(size > 0){
            if(ftruncate(fd, size)){};
		}
        else
            size = lseek(fd, 0L, SEEK_END);

        if(size > 0)
            map = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
        ::close(fd);
    } else if(size > 0) {
        map = mmap(0, size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS, -1, 0);
    }

    if(map == MAP_FAILED)
    {
        map = NULL;
    }

    return map;
}

int CTxnStatTool::open (const char* statfile, const char* cgixmlfile, const char* output_stat_file, int check_interval)
{
    if(TGlobal::DaemonInit(cgixmlfile) < 0)
		return -1;

    _cgi_control = TCGIControl::Instance ();

    if (NULL == _cgi_control)
    {
        fprintf (stderr, "get TCGIControl instance failed, msg[%s]\n", strerror(errno));

        return -1;
    }

    if (_cgi_control->open(cgixmlfile) == -1)
    {
        fprintf (stderr, "TCGIControl init failed, msg[%s]\n", strerror(errno));

        return -1;
    }

    _statinfo = (TStatHeadInfo*) map_file (statfile, 0);

    if (NULL == _statinfo)
    {
        fprintf (stderr, "get stat info failed, msg[%s]\n", strerror(errno));

        return -1;
    }

    if(NULL == output_stat_file)
    {
        fprintf(stderr, "output stat file is invalid\n");
        return -1;
    }
    strncpy(_output_file, output_stat_file, sizeof(_output_file)-1);

    _check_interval = check_interval;

    return 0;
}

int CTxnStatTool::close (void)
{
    return 0;
}

void CTxnStatTool::write_msg(const char *format, ...)
{
	int savedErrNo = errno;
	int offset= 0;
	char buf[MSG_SIZE];

	// formatted message
	{
		va_list ap;
		va_start(ap, format);
		// restore errno
		errno = savedErrNo;
		offset += vsnprintf(buf+offset, MSG_SIZE-offset-1, format, ap);
		va_end(ap);
	}

    if(offset > MSG_SIZE -1)
        offset = MSG_SIZE - 1;

    if(buf[offset - 1] != '\n')
    {
        buf[offset++] = '\n';
    }

    int fd = ::open(_output_file, O_CREAT | O_LARGEFILE | O_APPEND | O_WRONLY, 0644);

    if (fd >= 0)
    {
        if(write(fd, buf, offset)){};
        ::close(fd);
    }
}

int CTxnStatTool::print (void)
{ 
    u_long rate = 0;
    struct tm tm;
    time_t now = time (NULL);
    localtime_r(&now, &tm);

    write_msg("#[%02d:%02d:%02d]section begin", tm.tm_hour, tm.tm_min, tm.tm_sec);

    //print web request rate
    if (last_web_req_count > 0 && _statinfo->_web_req_count > last_web_req_count)
    {
        //print web request
        write_msg("Web_Total_Req=%lu", (u_long)(_statinfo->_web_req_count - last_web_req_count));
        rate = (_statinfo->_web_req_count - last_web_req_count) / _check_interval;
        write_msg("Web_Req_Rate=%lu r/s", rate);
    }
    last_web_req_count = _statinfo->_web_req_count;

    //print web response rate
    rate = 0;
    if (last_web_rsp_count > 0 && _statinfo->_web_rsp_count > last_web_rsp_count)
    {
        //print web response 
        write_msg("Web_Total_Rsp=%lu", (u_long)(_statinfo->_web_rsp_count - last_web_rsp_count));
        rate = (_statinfo->_web_rsp_count - last_web_rsp_count) / _check_interval;
        write_msg("Web_Rsp_Rate=%lu r/s", rate);
    }
    last_web_rsp_count = _statinfo->_web_rsp_count;

        //print helper request rate
    rate = 0;
    if (last_helper_req_count > 0 && _statinfo->_helper_req_count > last_helper_req_count)
    {
        //print helper request
        write_msg("Helper_Total_Req=%lu", (u_long)(_statinfo->_helper_req_count - last_helper_req_count));
        rate = (_statinfo->_helper_req_count - last_helper_req_count) / _check_interval;
        write_msg("Helper_Req_Rate=%lu r/s", rate);
    }
    last_helper_req_count = _statinfo->_helper_req_count;

    //print helper response rate
    rate = 0;
    if(last_helper_rsp_count > 0 && _statinfo->_helper_rsp_count > last_helper_rsp_count)
    {
        //print helper response
        write_msg("Helper_Total_Rsp=%lu", (u_long)(_statinfo->_helper_rsp_count));
        rate = (_statinfo->_helper_rsp_count - last_helper_rsp_count) / _check_interval;
        write_msg("Helper_Rsp_Rate=%lu r/s", rate);
    }
    last_helper_rsp_count = _statinfo->_helper_rsp_count;

    TCGIInfo* cgi_list = _cgi_control->get_cgi_info ();
    int cgi_count = _cgi_control->get_cgi_count ();

    TCGIStatData* cgi_stat_data = (TCGIStatData*) (_statinfo->_cgi_data);

    for (int i = 0; i < cgi_count; ++i)
    {
        //print cgi request rate
        rate = 0;
        if (last_cgi_req_count[i] > 0 && cgi_stat_data[i]._cgi_req_count > last_cgi_req_count[i])
        { 
            //print cgi total request
            write_msg("%s_Total_Req=%lu", cgi_list[i]._szCgiName, (u_long)(cgi_stat_data[i]._cgi_req_count - last_cgi_req_count[i]));
            rate = (cgi_stat_data[i]._cgi_req_count - last_cgi_req_count[i]) / _check_interval;
            write_msg("%s_Req_Rate=%lu r/s", cgi_list[i]._szCgiName, rate);
        }
        last_cgi_req_count[i] = cgi_stat_data[i]._cgi_req_count;

        //print cgi response rate
        rate = 0;
        if(last_cgi_rsp_count[i] > 0 && cgi_stat_data[i]._cgi_rsp_count > last_cgi_rsp_count[i])
        {
            //print cgi total response
            write_msg("%s_Total_Rsp=%lu", cgi_list[i]._szCgiName, (u_long)(cgi_stat_data[i]._cgi_rsp_count - last_cgi_rsp_count[i]));
            rate = (cgi_stat_data[i]._cgi_rsp_count - last_cgi_rsp_count[i]) / _check_interval;
            write_msg("%s_Rsp_Rate=%lu r/s", cgi_list[i]._szCgiName, rate);
        }
        last_cgi_rsp_count[i] = cgi_stat_data[i]._cgi_rsp_count;
    }
    write_msg("#section end");

    return 0;
}

HTTP_SVR_NS_END

USING_HTTP_SVR_NS;

int StopRun = 0;

void sig_handler(int sig)
{
    StopRun = 1;
}

int main (int argc, char* argv[])
{
    int c;
    char stat_file[256]         = {'\0'};
    char cgi_conf_file[256]     = {'\0'};
    char output_stat_file[256]  = {'\0'};
    int  check_interval         = 0;
    int  file_max_size          = 20;
    
    CTxnStatTool* pTxnStater = CTxnStatTool::Instance ();

    if (NULL == pTxnStater)
    {
        fprintf (stderr, "get CTxnStatTool instance failed.\n");
        return -1;
    }

    if (argc < 4)
    {
        fprintf (stderr, "usage: %s -f <stat file> -x config.xml -o /data/output_file -t 60 -m 20\n", argv[0]);

        return 0;
    }

    while ((c = getopt (argc, argv, "f:x:t:o:m:h")) != -1) {
		switch (c) {
		case 'f':
			strncpy (stat_file, optarg, sizeof (stat_file) - 1);
            if (access(stat_file, F_OK) != 0)
            {
                fprintf (stderr, "stat file [%s] read failed, msg[%s]\n", stat_file, strerror(errno));
                return -1;
            }
			break;
		case 'x':
			strncpy (cgi_conf_file, optarg, sizeof (cgi_conf_file) - 1);
            if (access(cgi_conf_file, F_OK) != 0)
            {
                fprintf (stderr, "cgi confi file [%s] read failed, msg[%s]\n", cgi_conf_file, strerror(errno));
                return -1;
            }
			break;
        case 't':
            check_interval = atoi(optarg) < 0 ? 60 : atoi(optarg); 
            break;
        case 'o':
            strncpy(output_stat_file, optarg, sizeof(output_stat_file) -1);
            break;
        case 'm':
            file_max_size = atoi(optarg) < 0 ? 20 : atoi(optarg);
            break;
		case 'h':
            fprintf (stderr, "usage: %s -f <stat file> -x cgilist.xml\n", argv[0]);
			return 0;
		default:
            fprintf (stderr, "usage: %s -f <stat file> -x cgilist.xml\n", argv[0]);
			return 0;
		}
	}

    if (pTxnStater->open(stat_file, cgi_conf_file, output_stat_file, check_interval) < 0)
    {
        fprintf (stderr, "init CTxnStatTool object failed.");
        return -1;
    }
    
    struct sigaction sa;

    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = sig_handler;
    sigaction(SIGINT, &sa, NULL);
    sigaction(SIGTERM, &sa, NULL);
    sigaction(SIGQUIT, &sa, NULL);
    sigaction(SIGHUP, &sa, NULL);

    signal(SIGPIPE,SIG_IGN);
    signal(SIGCHLD,SIG_IGN);
    
    if(daemon(1,1));

    struct stat sbuf;
    while(!StopRun)
    {
        if(0 == stat(output_stat_file, &sbuf))
        {    
            if((int)sbuf.st_size <= file_max_size * 1024 * 1024)
                pTxnStater->print();
        }
        else if(errno == ENOENT)
        {
            pTxnStater->print();
        }
        
        sleep (check_interval);
    }

    return 0;
}
